from django.shortcuts import HttpResponse
from django.db import transaction
from django.db.models.query import QuerySet
from django.views import View
from utilss.loggers import get_logger
from db_access import models
from copy import deepcopy
import traceback
import json

logger = get_logger()


class Agent(View):
    def post(self, request):
        try:
            # 从agent接收的新数据
            server_info = json.loads(request.body)
            # 从服务器中获取的旧数据, [] or data
            server_obj = models.Server.objects.filter(hostname=server_info['basic']['data']['hostname'])
            msgs = self.initializer(server_obj, server_info)
            # 日志记录
            if msgs:
                models.AssetsChange.objects.create(content=msgs, server_fk=server_obj.first())
        except Exception:
            logger.error(traceback.format_exc())

        return HttpResponse('数据记录成功！')

    def initializer(self, server_obj, server_info):
        """
        :param server_obj: 从服务器中获取的服务器旧数据，QuerySet对象
        :param server_info: 从agent接收的所有数据
        :return: 返回日志信息
        """
        msgs = ''
        try:
            server_dict_new = {}  # 服务器新数据

            server_dict_new.update(server_info['basic']['data'])
            server_dict_new.update(server_info['board']['data'])
            server_dict_new.update(server_info['cpu']['data'])

            if server_obj.first():
                server_dict_old = server_obj.values()[0]  # 服务器旧数据
                server_dict_old.pop('id')
                server_dict_old.pop('assets_fk_id')

                if server_dict_old != server_dict_new:
                    # 如果发生数据改变，那么更新数据库，且记录日志
                    server_obj.update(**server_dict_new)
                    for index, value in server_dict_old.items():
                        if value != server_dict_new[index]:
                            msgs += '原数据: {} 变更后: {} <br><br>'.format(index, server_dict_new[index])
                for base in ['disk', 'memory', 'nic']:
                    base_list_old = self.get_list(server_obj, base, 'Queryset')
                    base_list_new = self.get_list(server_info, base, 'Dict')

                    if base_list_old != base_list_new:
                        base_obj = server_obj.first()
                        # 先删除数据库中旧数据，然后更新为新数据
                        with transaction.atomic():
                            if base == 'disk':
                                base_obj.disk_set.all().delete()
                            elif base == 'memory':
                                base_obj.memory_set.all().delete()
                            elif base == 'nic':
                                base_obj.nic_set.all().delete()
                            self.bluk_create_db(base, base_obj, server_info, base_list_new)

                    # 资源更新日志记录
                    msgs += self.assets_log(base_list_old, base_list_new)
            else:
                for k, v in server_info.items():
                    if server_info['basic']['status']:
                        if v['status']:
                            msgs += '新增 {} 数据<br><br>'.format(k)
                            if k == 'basic':
                                assets_obj = models.Assets.objects.create(assets_type=1)
                                server_obj = models.Server.objects.create(**v['data'],
                                                                          assets_fk=assets_obj)
                            elif k == 'board' or k == 'cpu':
                                self.bluk_create_db(k, server_obj, server_info, None)
                            else:
                                if k == 'disk' or k == 'memory' or k == 'nic':
                                    base_list_new = self.get_list(server_info, k, 'Dict')
                                else:
                                    base_list_new = None
                                self.bluk_create_db(k, server_obj, server_info, base_list_new)
                        else:
                            msg = '上传的 {} 信息中status=False，请检查脚本或服务器是否有误！<br>'.format(k)
                            models.ErrorLog.objects.create(server_fk=server_obj, error_content=msg)
                    else:
                        msg = 'hostname: {} 上传的 basic 信息中status=False，请检查脚本或服务器是否有误！<br>'.format(
                            server_info['basic']['data']['hostname'],
                        )
                        models.ErrorLog.objects.create(error_content=msg)
                        break
        except Exception:
            logger.error(traceback.format_exc())

        return msgs

    def assets_log(self, base_list_old, base_list_new, msgs=''):
        """
        :param base_list_old:
        :param base_list_new:
        :param msgs:
        :return:
        """
        # 资产变更日志记录-修改/减少
        for j in base_list_old[1]:
            base_old = base_list_old[0][j]
            base_new = base_list_new[0].get(j, 0)
            if base_old != base_new and base_new != 0:
                msgs += '原数据: {} <br>变更后: {}<br><br>'.format(
                    str(base_old), str(base_new))
            elif base_old != base_new and base_new == 0:
                msgs += '原数据: {} <br>变更后: 该槽位为空！<br><br>'.format(
                    str(base_old))

        # 资产变更日志记录-增加
        base_set = base_list_new[1] - base_list_old[1]
        for i_set in base_set:
            base_new = base_list_new[0][i_set]
            msgs += '原数据: 该槽位为空！<br>变更后: {}<br><br>'.format(
                str(base_new))
        return msgs

    def bluk_create_db(self, base, server_obj, server_info, base_list_new):
        """
        :param base: memory/disk/nic/board/cpu
        :param server_obj: 服务器旧信息的queryset对象
        :param server_info: 服务器的全部新信息
        :param base_list_new: memory/disk/nic 的数据
        :return: None
        """
        base_list = []
        if base == 'memory':
            for index, i in base_list_new[0].items():
                base_list.append(models.Memory(**i, server_fk=server_obj))
            models.Memory.objects.bulk_create(base_list)
        elif base == 'disk':
            for index, i in base_list_new[0].items():
                base_list.append(models.Disk(**i, server_fk=server_obj))
            models.Disk.objects.bulk_create(base_list)
        elif base == 'nic':
            for index, i in base_list_new[0].items():
                base_list.append(models.Nic(**i, server_fk=server_obj))
            models.Nic.objects.bulk_create(base_list)
        elif base == 'board' or base == 'cpu':
            models.Server.objects.filter(id=server_obj.id).update(**server_info[base]['data'])

    def get_list(self, data, base, mode):
        """
        对旧数据进行标准化
        :param data: 需要标准化的数据, server_obj / server_info
        :param base: 如果传入一个QuerySet对象，那么需要传入base
        :param mode: Queryset/Dict
        :return: 返回一个列表
        """
        base_list = [{}, {1000, }]
        data_list = deepcopy(base_list)
        if mode == 'Queryset' and isinstance(data, QuerySet):
            query = data.first()
            if base == 'disk':
                value_list = query.disk_set.all().values('slot', 'pd_type', 'capacity', 'model')
            elif base == 'memory':
                value_list = query.memory_set.all().values('slot', 'speed', 'capacity', 'model', 'manufacturer',
                                                           'sn')
            elif base == 'nic':
                value_list = query.nic_set.all().values('nic_name', 'status', 'mac', 'ipv4', 'ipv4_mask', 'ipv6')
            else:
                value_list = []
            for i in value_list:
                if base == 'disk':
                    index = int(i['slot'])
                elif base == 'memory':
                    index = int(i['slot'].split('#')[-1])
                else:
                    index = i['nic_name']
                data_list[0][index] = i
                data_list[1].add(index)
        elif mode == 'Dict':
            for index, value in data[base]['data'].items():
                if base == 'disk':
                    index = int(value['slot'])
                elif base == 'memory':
                    index = int(value['slot'].split('#')[-1])
                else:
                    value['nic_name'] = index
                data_list[0][index] = value
                data_list[1].add(index)
        data_list[1].remove(1000)
        return data_list

